package com.fengye.security.utils;

import com.fengye.security.config.AppProperties;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;
import java.util.stream.Collectors;

/**
 * jwt 工具类
 *
 * @author fengyexjtu@126.com
 * @date 2022年05月24日 9:39 PM
 */

@Component
@RequiredArgsConstructor
public class JwtUtil {
    public static final Key accessTokenKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);
    public static final Key refreshTokenKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);

    private final AppProperties appProperties;

    public String createAccessToken(UserDetails userDetails) {
        return createToken(userDetails, appProperties.getJwt().getAccessTokenExpiredTime(), accessTokenKey);
    }

    public String createRefreshToken(UserDetails userDetails) {
        return createToken(userDetails, appProperties.getJwt().getRefreshTokenExpiredTime(), refreshTokenKey);
    }

    private String createToken(UserDetails userDetails, Long timeToExpired, Key key) {
        return Jwts
                .builder()
                .setId("mooc")
                .setSubject(userDetails.getUsername())
                .claim("authorities", userDetails.getAuthorities().stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toList()))
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + timeToExpired))
                .signWith(key, SignatureAlgorithm.HS512).compact();
    }

    public String createTokenByAuthentication(Authentication authentication) {
        return Jwts
                .builder()
                .setId("mooc")
                .setSubject(authentication.getName())
                .claim("authorities", authentication.getAuthorities().stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toList()))
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 600000))
                .signWith(accessTokenKey, SignatureAlgorithm.HS512).compact();
    }

    public String getUsername(String token) {
        Claims claims = (Claims) Jwts.parserBuilder().setSigningKey(accessTokenKey).build().parse(token).getBody();
        return claims.getSubject();
    }

    public String createAccessTokenByRefreshToken(String refreshToken) {
        Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(JwtUtil.refreshTokenKey).build().parseClaimsJws(refreshToken);

        String subject = claimsJws.getBody().getSubject();
        return Jwts
                .builder()
                .setId("mooc")
                .setSubject(subject)
                .addClaims(claimsJws.getBody())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + appProperties.getJwt().getAccessTokenExpiredTime()))
                .signWith(accessTokenKey, SignatureAlgorithm.HS512).compact();
    }

    private boolean validJwtToken(String token, boolean validExpireTime) {
        try{
            // 校验refreshToken 和 accessToken
            Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(JwtUtil.refreshTokenKey).build().parseClaimsJws(token);
            return true;
        }catch (ExpiredJwtException | SignatureException | MalformedJwtException | UnsupportedJwtException |
                IllegalArgumentException e){
            if (!validExpireTime && e.getClass().isInstance(ExpiredJwtException.class)){
                return true;
            }
        }
        return false;
    }

    public boolean validAccessToken(String token){
        return this.validJwtToken(token, false); // 不校验过期
    }

    public boolean validRefreshToken(String token) {
        return this.validJwtToken(token, true); // 校验过期时间
    }
}
